<html>
    <head>
        <title>数据过滤  DataFiltering</title>
    </head>
    <body>
        <script>
            // 数据过滤  Data Filtering

                    /*
                        Volo.Abp.Data 包定义了在 "从数据库查询时" 自动过滤数据的服务。
                    */

                    /*
                        一、预定义过滤器   

                                ABP定义了一些开箱即用的过滤器。


                                1、 软删除过滤器 ISoftDelete

                                            用于将实体 "标记为已删除" 而不是实际删除它。

                                            实现ISoftDelete接口以使你的实体“软删除”。

                                            eg:

                                                using System;
                                                using Volo.Abp;
                                                using Volo.Abp.Domain.Entities;

                                                namespace Acme.BookStore
                                                {
                                                    public class Book : AggregateRoot<Guid>, ISoftDelete
                                                    {
                                                        public string Name { get; set; }

                                                        public bool IsDeleted { get; set; } // ISoftDelete 接口定义的属性
                                                    }
                                                }

                                            ISoftDelete定义了IsDeleted属性。

                                            当你使用存储库删除一本书时，ABP会自动将IsDeleted设置为true并保护它不被实际删除
                                            
                                            （如果需要，你也可以手动将IsDeleted属性设置为true）。

                                            此外，当你查询数据库时，它会自动过滤已删除的实体。


                                            ★、ISoftDelete过滤器默认启用，除非你明确禁用它，否则你无法从数据库中获取已删除的实体。请参阅下面的IDataFilter服务。



                                2、多租户过滤器 IMultiTenant

                                           多租户是创建SaaS应用程序的一种有效方式。

                                           创建多租户应用程序后，你通常希望在租户之间隔离数据。

                                           实现 IMultiTenant接口以使你的实体“多租户感知”。

                                                eg:
                                                    using System;
                                                    using Volo.Abp;
                                                    using Volo.Abp.Domain.Entities;
                                                    using Volo.Abp.MultiTenancy;

                                                    namespace Acme.BookStore
                                                    {
                                                        public class Book : AggregateRoot<Guid>, ISoftDelete, IMultiTenant // 多租户感知
                                                        {
                                                            public string Name { get; set; }

                                                            public bool IsDeleted { get; set; } //  ISoftDelete 接口定义的属性

                                                            public Guid? TenantId { get; set; } // IMultiTenant 接口定义的属性
                                                        }
                                                    }

                                                IMultiTenant接口定义了TenantId属性，然后用于自动过滤当前租户的实体。有关更多信息，请参阅多租户文档。

                    */

                    /*
                        二、IDataFilter服务， 启用/禁用数据过滤器  

                                   1、使用方法：

                                            你可以使用 IDataFilter 服务来控制过滤器。

                                                    eg:
                                                        using System;
                                                        using System.Collections.Generic;
                                                        using System.Threading.Tasks;
                                                        using Volo.Abp;
                                                        using Volo.Abp.Data;
                                                        using Volo.Abp.DependencyInjection;
                                                        using Volo.Abp.Domain.Repositories;

                                                        namespace Acme.BookStore
                                                        {
                                                            public class MyBookService : ITransientDependency
                                                            {
                                                                private readonly IDataFilter _dataFilter;
                                                                private readonly IRepository<Book, Guid> _bookRepository;

                                                                public MyBookService(
                                                                    IDataFilter dataFilter,
                                                                    IRepository<Book, Guid> bookRepository)
                                                                {
                                                                    _dataFilter = dataFilter; // 注入过滤器管理服务
                                                                    _bookRepository = bookRepository;
                                                                }

                                                                public async Task<List<Book>> GetAllBooksIncludingDeletedAsync()
                                                                {
                                                                    // 临时性禁用软删除过滤器
                                                                    using (_dataFilter.Disable<ISoftDelete>()) //  禁用软删除过滤器
                                                                    {
                                                                        return await _bookRepository.GetListAsync();
                                                                    }
                                                                }
                                                            }
                                                        }

                                                将IDataFilter服务注入你的类。

                                                在using语句中使用Disable方法创建一个代码块，其中ISoftDelete过滤器在其中被禁用。


                                                除了 IDataFilter.Disable<T>()方法外，IDataFilter还有以下方法：

                                                    ①、IDataFilter.Enable<T>()方法:

                                                                可用于启用过滤器。Enable和Disable方法可用于以嵌套方式定义内部作用域。
                                                                
                                                    ②、IDataFilter.IsEnabled<T>()方法：

                                                                可用于检查过滤器当前是否启用

                                            为了取保过滤器被重置为之前的状态，请确保在using语句块中使用 Disable 和 Enable 方法。 
                                        
                                            
                                2、通用IDataFilter服务  IDataFilter<T>:

                                            IDataFilter 有一个泛型版本，  IDataFilter<TFilter> 它根据"过滤器类型"注入更受限制的 和 显式的数据过滤器。

                                                eg:

                                                    using System;
                                                    using System.Collections.Generic;
                                                    using System.Threading.Tasks;
                                                    using Volo.Abp;
                                                    using Volo.Abp.Data;
                                                    using Volo.Abp.DependencyInjection;
                                                    using Volo.Abp.Domain.Repositories;

                                                    namespace Acme.BookStore
                                                    {
                                                        public class MyBookService : ITransientDependency
                                                        {
                                                            private readonly IDataFilter<ISoftDelete> _softDeleteFilter;
                                                            private readonly IRepository<Book, Guid> _bookRepository;

                                                            public MyBookService(
                                                                IDataFilter<ISoftDelete> softDeleteFilter,
                                                                IRepository<Book, Guid> bookRepository)
                                                            {
                                                                _softDeleteFilter = softDeleteFilter;
                                                                _bookRepository = bookRepository;
                                                            }

                                                            public async Task<List<Book>> GetAllBooksIncludingDeletedAsync()
                                                            {
                                                                // 暂时性禁用软删除过滤器
                                                                using (_softDeleteFilter.Disable())
                                                                {
                                                                    return await _bookRepository.GetListAsync();
                                                                }
                                                            }
                                                        }
                                                    }

                                            此用法在注入IDataFilter<T>服务时确定过滤器类型。

                                            在这种情况下，你可以使用Disable()和Enable()方法而无需指定过滤器类型。

                                            还是记得要在using中使用 Disable 和 Enable 方法，这样可以恢复到之前的过滤器状态。

                    */

                    /*
                        三、数据过滤器服务选项类  AbpDataFilterOptions
                        
                                ABP 的数据过滤系统的主要设置类就是 AbpDataFilterOptions 选项类。

                                下面的示例代码默认禁用ISoftDelete过滤器，这将导致你查询数据时包含已删除的实体，除非你显式启用过滤器：

                                        Configure<AbpDataFilterOptions>(options =>
                                        {
                                            // 设置默认状态
                                            options.DefaultStates[typeof(ISoftDelete)] = new DataFilterState(isEnabled: false);
                                        });  

                                在全局性更改过滤器状态的时候一定要谨慎，特别是当你使用的是预构建的模块时，因为，该模块可能会在默认情况下打开软删除过滤器的情况下开发的。

                                但是你可以安全的为自己定义的过滤器执行此操作。
                    */

                    /*
                        四、自定义过滤器  Defining Custom Filters

                                定义 和 实现新新过滤器在很大程度上取决于数据库提供程序。

                                ABP为所有数据库提供程序实现所有预定义的过滤器。

                                如何自定义过滤器：

                                1、第一步：定义过滤器接口：

                                        当你需要自定义过滤器时，首先为你的过滤器定义一个接口 （比如 ISoftDelete 和 IMultiTenant） 并为你的实体实现它。

                                                eg: 
                                                    public interface IIsActice
                                                    {
                                                        bool IsActive {get;set;} // 过滤器功能所依赖的过滤属性(表字段)
                                                    }

                                        这样的 IIsActive 接口可用于过滤 活动/非活动 数据，并且可以由任何实体轻松实现。

                                                eg:
                                                    public class Book : AggregateRoot<Guid>, IIsActive
                                                    {
                                                        public string Name { get; set; }

                                                        public bool IsActive { get; set; } // IIsActive过滤器接口定义的用于过滤的属性
                                                    }



                                2、EF Core 中配置过滤器：

                                        ABP 使用EF Core 的 "全局查询过滤系统" 来集成过滤器(预定义 / 自定义 )

                                        因此，即使你直接使用DbContext，它也可以很好地继承到EF Core 并按预期工作。

                                        实现自定义过滤器的最佳方法是为 DbContext 重写 "ShouldFilterEntity" 和 "CreateFilterExpression" 这连个方法：

                                            eg:

                                                // 查看全局配置，是否IsActive过滤器被配置为启用状态
                                                // 添加了IsActiveFilterEnabled属性来检查IIsActive是否启用。它在内部使用之前介绍的IDataFilter服务。
                                                protected bool IsActiveFilterEnabled => DataFilter?.IsEnabled<IIsActive>() ?? false;

                                                // 重写ShouldFilterEntity方法
                                                protected override bool ShouldFilterEntity<TEntity>(IMutableEntityType entityType)
                                                {
                                                    // 检查给定实体是否实现了IIsActive接口
                                                    if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity)))
                                                    {
                                                        return true;
                                                    }

                                                    return base.ShouldFilterEntity<TEntity>(entityType);
                                                }

                                                // 配置过滤器表达式
                                                protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>(ModelBuilder modelBuilder)
                                                {
                                                    // 创建基本表达式树，如果存在的话
                                                    var expression = base.CreateFilterExpression<TEntity>(modelBuilder);

                                                    if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) // 检查给定实体是否实现了IIsActive接口
                                                    {
                                                        // 构建过滤器表达式树
                                                        Expression<Func<TEntity, bool>> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property<bool>(e, "IsActive");
                                                        expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter);
                                                    }

                                                    return expression;
                                                }

                                                添加了IsActiveFilterEnabled属性来检查IIsActive是否启用。它在内部使用之前介绍的IDataFilter服务。

                                                覆盖了ShouldFilterEntity和CreateFilterExpression方法，检查给定实体是否实现了IIsActive接口，并在必要时组合表达式。


                                    ★、OnModelCreating方法中配置自定义过滤器：

                                               你可以使用HasAbpQueryFilter 为实体设置过滤器。它将你的过滤器与 ABP EF Core内置的全局过滤器相结合 (And 关系)
                                               
                                                    eg:
                                                            protected override void OnModelCreating(ModelBuilder modelBuilder)
                                                            {
                                                                base.OnModelCreating(modelBuilder);
                                                                
                                                                modelBuilder.Entity<MyEntity>(b =>
                                                                {
                                                                    b.HasAbpQueryFilter(e => e.Name.StartsWith("abp"));
                                                                });
                                                            }
                    */

                   
        </script>
    </body>
</html>